const Koa = require("koa");
const Router = require("koa-router");
const WebSocket = require("ws");
const fs = require("fs");
const path = require("path");
const axios = require("axios");

const app = new Koa();
const router = new Router();

// 模拟多个 HTTP 接口
router.get("/ws/internal/getdate", async (ctx) => {
  ctx.body = {
    date: new Date().toISOString(),
    message: "This is the response from the internal service.",
  };
});

app.use(router.routes()).use(router.allowedMethods());

// 启动 HTTP 服务
app.listen(3000, () => {
  console.log("Internal Koa server is listening on port 3000.");
});

// 连接到中间服务器的 WebSocket 服务
const wsUrl = "ws://47.122.68.107/ws/internal";
const ws = new WebSocket(wsUrl);

ws.on("open", () => {
  console.log("Connected to the middle server.");
});

// 建立连接之后，当中间服务器发送消息的时候触发
ws.on("message", async (message) => {
  console.log(`Received from middle server: ${message}`);

  // 解析请求消息
  const request = JSON.parse(message);
  console.log("Request:", request);

  try {
    // 确保 request 对象包含必要字段
    if (!request || !request.path) {
      throw new Error("Invalid request: missing path");
    }

    // 判断是否是静态资源请求
    if (request.path.startsWith("/ws/internal/static/")) {
      const filePath = path.join(
        __dirname,
        "../public",
        request.path.split("/static/")[1]
      );
      console.log("File Path:", filePath);

      try {
        // 同步读取文件内容
        // 这里是可以使用异步的写法的，但是这样有点麻烦
        // 直接使用同步读取文件的方式
        const data = fs.readFileSync(filePath);

        // 根据文件扩展名设置 MIME 类型
        const mimeType = getMimeType(filePath);
        console.log("MIME Type:", mimeType);

        // 获取文件状态信息
        const fileStats = fs.statSync(filePath);

        // 构造响应头
        const headers = {
          "Content-Type": mimeType,
          "Content-Length": fileStats.size,
          "Cache-Control": "max-age=3600",
          "Last-Modified": fileStats.mtime.toUTCString(),
          ETag: `"${fileStats.size}-${fileStats.mtime.getTime()}"`,
          "Accept-Ranges": "bytes",
        };

        // 发送元信息（JSON 格式）
        ws.send(
          JSON.stringify({
            id: request.id,
            data: {
              isFile: true,
              status: 200,
              headers,
              body: data.toString("base64"), // 将 Buffer 转换为 Base64 编码的字符串
            },
          })
        );

        console.log("Binary file sent successfully.");
      } catch (err) {
        console.error("Error reading file:", err.message);
        ws.send(
          JSON.stringify({
            id: request.id,
            data: {
              status: 404,
              headers: { "Content-Type": "text/plain" },
              body: "File Not Found",
            },
          })
        );
      }
      return;
    }

    // 能走到这里就说明不是上面的静态资源请求
    // 这里是动态接口请求
    // 构造目标 URL（动态接口）
    const targetUrl = `http://localhost:3000${request.path}`;

    // 使用 axios 调用本地 HTTP 接口
    const response = await axios({
      method: request.method,
      url: targetUrl,
      headers: request.headers,
      params: request.query,
    });

    // 将响应数据发送回中间服务器
    ws.send(
      JSON.stringify({
        id: request.id,
        data: {
          status: response.status,
          headers: response.headers,
          body: response.data,
        },
      })
    );
  } catch (error) {
    console.error("Error processing message:", error.message || error);

    // 返回错误信息给中间服务器
    ws.send(
      JSON.stringify({
        id: request ? request.id : null,
        data: {
          status: error.response ? error.response.status : 500,
          headers: { "Content-Type": "application/json" },
          body: { error: error.message || "Unknown error" },
        },
      })
    );
  }
});

// 在当内网服务器关闭websocket连接时，会触发的事件
ws.on("close", () => {
  console.log("Disconnected from the middle server.");
});

// 当发生错误时触发的事件
ws.on("error", (error) => {
  console.error(`WebSocket error: ${error.message}`);
});

// 根据文件扩展名获取 MIME 类型
function getMimeType(filePath) {
  const ext = path.extname(filePath).toLowerCase();
  switch (ext) {
    case ".html":
      return "text/html";
    case ".css":
      return "text/css";
    case ".js":
      return "application/javascript";
    case ".png":
      return "image/png";
    case ".jpg":
    case ".jpeg":
      return "image/jpeg";
    case ".gif":
      return "image/gif";
    default:
      return "application/octet-stream";
  }
}
